﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.IO;
using System.Text;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Remoting;
using System.Security.Permissions;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;

namespace test2
{
    class Program
    {
        public const string JOB_NAME = "EFWorkflowTimer";
        public const char JobName_SeparatorChar = ' ';

        public const string Configuration_ListTitle = @"EFWorkflowTimerCfg";
        public const string Configuration_ListDescription = @"Eric Fang's SharePoint 2010 WorkflowTimer configuration settings";
        public const string History_ListTitle = @"EFWorkflowTimerHistory";
        public const string History_ListDescription = @"Eric Fang's SharePoint 2010 WorkflowTimer History";

        public const string Configuration_ListUrl = @"/Lists/" + Configuration_ListTitle;
        public const string History_ListUrl = @"/Lists/" + History_ListTitle;

        public const string Column_Title = @"Title";
        public const string Column_LinkTitle = @"LinkTitle";

        //  EFWorkflowTimerCfg
        public const string Column_EFWorkflowTimerCfg_WorkflowType = @"WorkflowType";
        public const string Column_EFWorkflowTimerCfg_WorkflowName = @"WorkflowName";
        public const string Column_EFWorkflowTimerCfg_ViewUrl = @"ViewUrl";
        public const string Column_EFWorkflowTimerCfg_LastScanTime = @"LastScanTime";
        public const string Column_EFWorkflowTimerCfg_LastScanCount = @"LastScanCount";

        //  EFWorkflowTimerHistory
        public const string Column_EFWorkflowTimerHistory_WorkflowType = @"WorkflowType";
        public const string Column_EFWorkflowTimerHistory_WorkflowName = @"WorkflowName";
        public const string Column_EFWorkflowTimerHistory_WebTitle = @"WebTitle";
        public const string Column_EFWorkflowTimerHistory_WebUrl = @"WebUrl";
        public const string Column_EFWorkflowTimerHistory_ListTitle = @"ListTitle";
        public const string Column_EFWorkflowTimerHistory_ItemId = @"ItemId";
        public const string Column_EFWorkflowTimerHistory_ItemUrl = @"ItemUrl";
        public const string Column_EFWorkflowTimerHistory_Modified = @"Modified";

        public const string WorkflowType_Site = @"Site";
        public const string WorkflowType_List = @"List";

        public const string DocLib_SitePages = @"SitePages";
        public const string PageName_AdminPage = @"EFWorkflowTimerAdminLink";
        public const string Url_AdminPage = "SitePages/EFWorkflowTimerAdminLink.aspx";

        public const string Zone_TitleBar = @"TitleBar";
        public const string Zone_Header = @"Header";
        public const string Zone_LeftColumn = @"LeftColumn";
        public const string Zone_MiddleColumn = @"MiddleColumn";
        public const string Zone_RightColumn = @"RightColumn";
        public const string Zone_Footer = @"Footer";

        public static string _strJobItemName = string.Empty;

        public static void logDebugHistory(SPWeb objSPWeb, string strMessage)
        {
            logDebugHistory(objSPWeb, strMessage, Assembly.GetExecutingAssembly());
        }

        public static void logDebugHistory(SPWeb objSPWeb, string strMessage, string strWorkflowType, string strWorkflowName)
        {
            logDebugHistory(objSPWeb, strMessage, Assembly.GetExecutingAssembly(), strWorkflowType, strWorkflowName);
        }

        public static void logDebugHistory(SPWeb objSPWeb, string strMessage, System.Reflection.Assembly objAssembly)
        {
            if (string.IsNullOrEmpty(strMessage))
                return;

            logDebugHistory(objSPWeb, strMessage, objAssembly, string.Empty, string.Empty);
        }

        public static void logDebugHistory(SPWeb objSPWeb, string strMessage, System.Reflection.Assembly objAssembly, string strWorkflowType, string strWorkflowName)
        {
            if (string.IsNullOrEmpty(strMessage))
                return;

            string strLogMessage = strMessage;
            //if (strLogMessage.Length > 250)
            //    strLogMessage = strLogMessage.Substring(0, 250);

            SPList splistHistory = objSPWeb.GetList(getServerRelativeUrl(objSPWeb, History_ListUrl));

            SPListItem itemHistory = splistHistory.AddItem();
            FileInfo objFileInfo = new FileInfo(objAssembly.Location);
            itemHistory[Column_Title] = string.Format(@"debug - {0} - {1}", objFileInfo.CreationTime, objAssembly.GetName().Version);
            itemHistory[Column_EFWorkflowTimerHistory_WorkflowType] = strWorkflowType;
            itemHistory[Column_EFWorkflowTimerHistory_WorkflowName] = strWorkflowName;
            itemHistory[Column_EFWorkflowTimerHistory_WebTitle] = strLogMessage;
            itemHistory[Column_EFWorkflowTimerHistory_WebUrl] = objSPWeb.ServerRelativeUrl;
            itemHistory.Update();
        }

        public static void logDebugHistory(string strMessage)
        {
            if (string.IsNullOrEmpty(strMessage))
                return;

            SPDiagnosticsService diagSvc = SPDiagnosticsService.Local;
            diagSvc.WriteTrace(0,
                new SPDiagnosticsCategory(JOB_NAME + " category", TraceSeverity.Monitorable, EventSeverity.Error),
                TraceSeverity.Monitorable,
                "{0}",
                new object[] { strMessage });
        }

        public static void logWorkflowTimerHistory(string strJobName, SPWeb objSPWeb, SPListItem itemTarget, string strWorkflowType, string strWorkflowName)
        {
            logWorkflowTimerHistory(Assembly.GetExecutingAssembly(), strJobName, objSPWeb, itemTarget, strWorkflowType, strWorkflowName);
        }

        public static void logWorkflowTimerHistory(System.Reflection.Assembly objAssembly, string strJobName, SPWeb objSPWeb, SPListItem itemTarget, string strWorkflowType, string strWorkflowName)
        {
            string strTitle = string.Empty;
            SPList splistHistory = objSPWeb.GetList(getServerRelativeUrl(objSPWeb, History_ListUrl));

            SPListItem itemHistory = splistHistory.AddItem();
            FileInfo objFileInfo = new FileInfo(objAssembly.Location);
            strTitle = string.Format(@"{0} - {1} - {2}", strJobName, objFileInfo.CreationTime, objAssembly.GetName().Version);
            itemHistory[Column_Title] = strTitle;
            itemHistory[Column_EFWorkflowTimerHistory_WorkflowType] = strWorkflowType;
            itemHistory[Column_EFWorkflowTimerHistory_WorkflowName] = strWorkflowName;
            itemHistory[Column_EFWorkflowTimerHistory_WebTitle] = objSPWeb.Title;
            itemHistory[Column_EFWorkflowTimerHistory_WebUrl] = objSPWeb.ServerRelativeUrl;
            if (itemTarget != null)
            {
                itemHistory[Column_EFWorkflowTimerHistory_ListTitle] = itemTarget.ParentList.Title;
                itemHistory[Column_EFWorkflowTimerHistory_ItemId] = itemTarget.ID;
                itemHistory[Column_EFWorkflowTimerHistory_ItemUrl] = itemTarget.Url;
            }
            itemHistory.Update();
        }

        public static string getFullJobName(string strUrl)
        {
            string strReturn = strUrl;
            strReturn = strReturn.Substring(strReturn.IndexOf(@"//") + 2);
            strReturn = strReturn.Replace(@":", @"_");
            strReturn = strReturn.Replace(@"/", @"_");
            strReturn = string.Format(@"{0} - {1}", JOB_NAME, strReturn);

            return strReturn;
        }

        public static string getServerRelativeUrl(SPWeb objSPWeb, string strWebRelativeUrl)
        {
            string strServerRelativeUrl = string.Empty;
            string strWebServerRelativeUrl = objSPWeb.ServerRelativeUrl;
            if (strWebServerRelativeUrl == @"/") strWebServerRelativeUrl = string.Empty;

            strServerRelativeUrl = strWebServerRelativeUrl + strWebRelativeUrl;
            return strServerRelativeUrl;
        }

        public static string replaceTags(SPListItem itemTarget, string strContent)
        {
            return replaceTags(itemTarget, strContent, false);
        }

        public static string replaceTags(SPListItem itemTarget, string strContent, bool boolUserEmail)
        {
            string strReturn = strContent;
            string strTag = string.Empty;
            string strValue = string.Empty;
            int iCfgItemId = itemTarget.ID;

            using (SPWeb objSPWeb = itemTarget.Web)
            {
                foreach (SPField objSPField in itemTarget.Fields)
                {
                    try
                    {
                        strTag = string.Format("[{0}]", objSPField.Title);
                        if (strReturn.IndexOf(strTag, StringComparison.InvariantCultureIgnoreCase) < 0)
                            continue;

                        strValue = string.Empty;
                        if (objSPField.Type == SPFieldType.Lookup)
                        {
                            SPFieldLookupValue objLookupValue = (SPFieldLookupValue)itemTarget[objSPField.Title];
                            strValue = objLookupValue.LookupValue;
                        }
                        else if (objSPField.Type == SPFieldType.User)
                        {
                            SPFieldUserValue objUserValue = new SPFieldUserValue(objSPWeb, Convert.ToString(itemTarget[objSPField.Title]));
                            if (boolUserEmail)
                            {
                                strValue = objUserValue.User.Email;
                            }
                            else
                            {
                                strValue = objUserValue.User.Name;
                            }
                        }
                        else if (objSPField.Type == SPFieldType.URL)
                        {
                            SPFieldUrlValue objUrlValue = (SPFieldUrlValue)itemTarget[objSPField.Title];
                            strValue = string.Format(@"<a href='{0}'>{1}</a>", objUrlValue.Url, objUrlValue.Description);
                        }
                        else
                        {
                            strValue = Convert.ToString(itemTarget[objSPField.Title]);
                        }
                        strReturn = strReturn.Replace(strTag, strValue);
                    }
                    catch (Exception ex)
                    {
                        logDebugHistory(objSPWeb, string.Format(@"ex, list title={0}, item id={1}, objSPField.Title={2}, strValue={3}, ItemId=", itemTarget.ParentList.Title, itemTarget.ID, objSPField.Title, strValue, iCfgItemId));
                        logDebugHistory(objSPWeb, string.Format(@"ex.Message={0}", ex.Message));
                        logDebugHistory(objSPWeb, string.Format(@"ex.StackTrace={0}", ex.StackTrace));

                        continue;
                    }
                }
            }

            return strReturn;
        }

        public static void processOneListWorkflow(SPWeb objSPWeb, SPListItem itemTarget, string strWorkflowName)
        {
            SPWorkflowAssociationCollection objWorkflowAssociationCollection = null;

            try
            {
                logDebugHistory(objSPWeb, string.Format(@"processOneListWorkflow(), strWorkflowName={0}, itemTarget.Url={1}", strWorkflowName, itemTarget.Url), WorkflowType_List, strWorkflowName);

                using (SPSite site = new SPSite(objSPWeb.Site.ID))
                {
                    using (SPWeb web = site.OpenWeb(objSPWeb.ID))
                    {
                        SPWorkflowCollection objSPWorkflowCollection = site.WorkflowManager.GetItemActiveWorkflows(itemTarget);
                        foreach (SPWorkflow objSPWorkflow in objSPWorkflowCollection)
                        {
                            if (objSPWorkflow.ItemName.Equals(strWorkflowName, StringComparison.InvariantCultureIgnoreCase))
                                return;
                        }
                        objWorkflowAssociationCollection = itemTarget.ParentList.WorkflowAssociations;
                        foreach (SPWorkflowAssociation objWorkflowAssociation in objWorkflowAssociationCollection)
                        {
                            if (objWorkflowAssociation.Name.Equals(strWorkflowName, StringComparison.InvariantCultureIgnoreCase))
                            {
                                site.WorkflowManager.StartWorkflow(itemTarget, objWorkflowAssociation, objWorkflowAssociation.AssociationData, true);
                            }
                        }
                    }
                }

                logWorkflowTimerHistory(_strJobItemName, objSPWeb, itemTarget, WorkflowType_List, strWorkflowName);
            }
            catch (Exception ex)
            {
                logDebugHistory(objSPWeb, string.Format(@"ex, list title={0}, item id={1}", itemTarget.ParentList.Title, itemTarget.ID), WorkflowType_List, strWorkflowName);
                logDebugHistory(objSPWeb, string.Format(@"ex.Message={0}", ex.Message), WorkflowType_List, strWorkflowName);
                logDebugHistory(objSPWeb, string.Format(@"ex.StackTrace={0}", ex.StackTrace), WorkflowType_List, strWorkflowName);

                throw;
            }
        }

        public static void processOneSiteWorkflow(SPWeb objSPWeb, string strWorkflowName)
        {
            try
            {
                logDebugHistory(objSPWeb, string.Format(@"processOneSiteWorkflow()"), WorkflowType_Site, strWorkflowName);

                using (SPSite site = new SPSite(objSPWeb.Site.ID))
                {
                    using (SPWeb web = site.OpenWeb(objSPWeb.ID))
                    {
                        //find workflow to start
                        SPWorkflowAssociation objSPWorkflowAssociation = web.WorkflowAssociations.GetAssociationByName(strWorkflowName, CultureInfo.InvariantCulture);

                        //this is the call to start the workflow
                        SPWorkflow objSPWorkflow = site.WorkflowManager.StartWorkflow(null, objSPWorkflowAssociation, string.Empty, SPWorkflowRunOptions.Synchronous);
                    }
                }

                logWorkflowTimerHistory(_strJobItemName, objSPWeb, null, WorkflowType_Site, strWorkflowName);
            }
            catch (Exception ex)
            {
                logDebugHistory(objSPWeb, string.Format(@"ex, objSPWeb.ServerRelativeUrl={0}", objSPWeb.ServerRelativeUrl), WorkflowType_Site, strWorkflowName);
                logDebugHistory(objSPWeb, string.Format(@"ex.Message={0}", ex.Message), WorkflowType_Site, strWorkflowName);
                logDebugHistory(objSPWeb, string.Format(@"ex.StackTrace={0}", ex.StackTrace), WorkflowType_Site, strWorkflowName);

                throw;
            }
        }

        public static void markOneSPListItem(SPWeb objSPWeb, SPListItem itemTarget, int iItemCount)
        {
            itemTarget[Column_EFWorkflowTimerCfg_LastScanTime] = DateTime.Now;
            itemTarget[Column_EFWorkflowTimerCfg_LastScanCount] = iItemCount;
            itemTarget.SystemUpdate(false);
        }

        public static void processOneJobitem(SPWeb objSPWeb, SPListItem objJobitem)
        {
            string strWorkflowName = string.Empty;
            SPView spviewTarget = null;
            SPList splistTarget = null;
            SPListItemCollection objSPListItemCollection = null;

            string strViewUrl = string.Empty;
            string strWorkflowType = string.Empty;

            try
            {
                logDebugHistory(objSPWeb, string.Format(@"processOneJobitem() begin. objJobitem.Url={0}", objJobitem.Url), string.Empty, strWorkflowName);

                _strJobItemName = objJobitem.Name;
                strViewUrl = Convert.ToString(objJobitem[Column_EFWorkflowTimerCfg_ViewUrl]);
                SPFieldChoice objWorkflowType = (SPFieldChoice)objJobitem.Fields[Column_EFWorkflowTimerCfg_WorkflowType];
                strWorkflowType = objWorkflowType.GetFieldValueAsText(objJobitem[Column_EFWorkflowTimerCfg_WorkflowType]);
                strWorkflowName = Convert.ToString(objJobitem[Column_EFWorkflowTimerCfg_WorkflowName]);

                if (string.IsNullOrEmpty(strViewUrl) && strWorkflowType.Equals(WorkflowType_Site, StringComparison.InvariantCultureIgnoreCase))
                {
                    processOneSiteWorkflow(objSPWeb, strWorkflowName);
                }
                else
                {
                    spviewTarget = objSPWeb.GetViewFromUrl(getServerRelativeUrl(objSPWeb, strViewUrl));
                    splistTarget = spviewTarget.ParentList;
                    objSPListItemCollection = splistTarget.GetItems(spviewTarget);

                    foreach (SPListItem itemTarget in objSPListItemCollection)
                    {
                        try
                        {
                            processOneListWorkflow(objSPWeb, itemTarget, strWorkflowName);
                            markOneSPListItem(objSPWeb, objJobitem, objSPListItemCollection.Count);
                        }
                        catch (Exception ex)
                        {
                            logDebugHistory(objSPWeb, string.Format(@"objJobitem.Url={0}, objSPListItemCollection.Count={1}", objJobitem.Url, objSPListItemCollection.Count), strWorkflowType, strWorkflowName);
                            logDebugHistory(objSPWeb, string.Format(@"itemTarget.Url={0}", itemTarget.Url), strWorkflowType, strWorkflowName);
                            logDebugHistory(objSPWeb, string.Format(@"ex.Message={0}", ex.Message), strWorkflowType, strWorkflowName);
                            logDebugHistory(objSPWeb, string.Format(@"ex.StackTrace={0}", ex.StackTrace), strWorkflowType, strWorkflowName);

                            continue;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                logDebugHistory(objSPWeb, string.Format(@"ex, objSPWeb.ServerRelativeUrl={0}, objJobitem.Url={1}", objSPWeb.ServerRelativeUrl, objJobitem.Url), strWorkflowType, strWorkflowName);
                logDebugHistory(objSPWeb, string.Format(@"ex.Message={0}", ex.Message), strWorkflowType, strWorkflowName);
                logDebugHistory(objSPWeb, string.Format(@"ex.StackTrace={0}", ex.StackTrace), strWorkflowType, strWorkflowName);

                throw;
            }
        }

        public static void processOneSiteCollection(SPSite objSPSite)
        {
            SPList splistJob = null;
            SPList splistHistory = null;

            using (SPWeb objSPWeb = objSPSite.RootWeb)
            {
                try
                {
                    splistHistory = objSPWeb.Lists.TryGetList(History_ListTitle);
                    if (splistHistory == null)
                        return;

                    string strFullJobName = getFullJobName(objSPSite.Url);
                    logDebugHistory(@"EFWorkflowTimer executed. FullJobName=" + strFullJobName + ", objSPWeb.ServerRelativeUrl=" + objSPWeb.ServerRelativeUrl);

                    splistJob = objSPWeb.GetList(getServerRelativeUrl(objSPWeb, Configuration_ListUrl));
                    logDebugHistory(@"EFWorkflowTimer executed. splistJob.RootFolder.ServerRelativeUrl=" + splistJob.RootFolder.ServerRelativeUrl);
                    foreach (SPListItem objJobitem in splistJob.Items)
                    {
                        try
                        {
                            processOneJobitem(objSPWeb, objJobitem);
                        }
                        catch
                        {
                            continue;
                        }
                    }
                }
                catch (Exception ex)
                {
                    logDebugHistory(objSPWeb, string.Format(@"processOneSiteCollection(), ex, Configuration_ListUrl={0}, objSPSite.ServerRelativeUrl={1}", getServerRelativeUrl(objSPWeb, Configuration_ListUrl), objSPSite.ServerRelativeUrl));
                    logDebugHistory(objSPWeb, string.Format(@"processOneSiteCollection(), ex.Message={0}", ex.Message));
                    logDebugHistory(objSPWeb, string.Format(@"processOneSiteCollection(), ex.StackTrace={0}", ex.StackTrace));

                    throw;
                }
            }
        }

        static void Main(string[] args)
        {
            using (SPSite objSPSite = new SPSite(@"http://hvsp2010a:8080"))
            {
                Console.WriteLine(objSPSite.Url);
                //processOneSiteCollection(objSPSite);
            }

            Console.WriteLine("Completed. Press enter key to exit.");
            Console.ReadLine();
        }
    }
}
